Analyze and visualize the food inflation and wage growth in Indonesia from 2010 to 2021.
1. Install and load the necessary library.
library(RMySQL)
Loading required package: DBI
library(tidyverse)
── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
✔ ggplot2 3.3.6 ✔ purrr 0.3.4
✔ tibble 3.1.7 ✔ dplyr 1.0.9
✔ tidyr 1.2.0 ✔ stringr 1.4.0
✔ readr 2.1.2 ✔ forcats 0.5.1
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
library(DT)
library(gganimate)
library(ggimage)
RMySQL package is needed to connect to MySQL database. Tidyverse package is used for data processing and visualization. DT improves the quality of the table display on the HTML page.
2. Connect to local MySQL server.
mydb <- dbConnect(RMySQL::MySQL(),
dbname='commodity_price',
host='localhost',
port=3306,
user = user,
password = password)
4. Write a query to select all rows and columns from the food_commodity_price and minimum_wage table, then show the results.
result_1 <- dbGetQuery(mydb, "SELECT * FROM food_commodity_price")
datatable(
result_1,
extensions = "FixedColumns",
options = list(
paging = TRUE, searching = TRUE,
scrollX = TRUE, fixedColumns = list(leftColumns = 2)
)
)
result_2 <- dbGetQuery(mydb, "SELECT * FROM minimum_wage")
datatable(
result_2,
extensions = "FixedColumns",
options = list(
paging = TRUE, searching = TRUE,
scrollX = TRUE, fixedColumns = list(leftColumns = 2)
)
)
6. Show the results of the view.
result_3 <- dbGetQuery(mydb, "SELECT * FROM fcp_long")
datatable(
result_3,
extensions = 'Scroller',
options = list(
deferRender = TRUE,
scrollY = 200,
scroller = TRUE
)
)
7. For visualization purpose, the food commodity with any null value in the price column will be excluded.
result_4 <- dbGetQuery(mydb, "SELECT food_commodity FROM fcp_long GROUP BY 1 HAVING MIN(price_per_kg) <> 0")
result_4
The following food commodities have no null value in the price column: shallot (bawang merah), rice (beras), red chili (cabe merah), curly red chili (cabe merah keriting), chicken meat (daging ayam broiler), beef (daging sapi), white sugar (gula pasir lokal), cooking oil (minyak goreng kemasan), and chicken egg (telur ayam ras). I am going to exclude curly red chili because it’s very similar to red chili.
8. Combine the minimum wage data with the food price using UNION. Then calculate the YoY and cumulative growth percentage for food’s price and minimum wage.
food_wage_df <- dbGetQuery(mydb, "WITH food_wage AS (SELECT *
FROM fcp_long
WHERE food_commodity <> 'Cabe Merah Keriting'
AND food_commodity IN (SELECT food_commodity
FROM fcp_long
GROUP BY 1
HAVING MIN(price_per_kg) <> 0)
UNION
SELECT nama_variabel, nama_tahun, data_content
FROM minimum_wage
ORDER BY 1, 2)
SELECT food_commodity AS food_or_wage, CAST(year AS SIGNED) as year,
price_per_kg AS price_per_kg_or_minimum_wage,
(price_per_kg - (LAG(price_per_kg) OVER (PARTITION BY food_commodity ORDER BY year))) /
(LAG(price_per_kg) OVER (PARTITION BY food_commodity ORDER BY year)) * 100 AS growth_percentage,
(price_per_kg - (FIRST_VALUE(price_per_kg) OVER (PARTITION BY food_commodity ORDER BY year))) /
(FIRST_VALUE(price_per_kg) OVER (PARTITION BY food_commodity ORDER BY year)) * 100 AS cumulative_gp
FROM food_wage")
Warning in .local(conn, statement, ...): Decimal MySQL column 3 imported as
numeric
Warning in .local(conn, statement, ...): Decimal MySQL column 4 imported as
numeric
datatable(
food_wage_df,
extensions = 'Scroller',
options = list(
deferRender = TRUE,
scrollY = 200,
scroller = TRUE
)
)
CAST function is used to change the year to signed integer. This allows R to save them as dbl (double-precision floating point number) instead of chr (characters).
9. Visualize the cumulative growth percentage for food’s price and minimum wage since 2010.
food_wage_df[is.na(food_wage_df)] <- 0
food_wage_df %>%
ggplot(aes(cumulative_gp, food_or_wage)) +
geom_col(aes(fill = food_or_wage)) +
facet_wrap(vars(year))

Make a new dataframe to rank the food commodities and wage based on the cumulative percent growth for each year, and translate the items from Indonesian to English.
food_wage_df2 <- food_wage_df %>%
group_by(year) %>%
arrange(year, desc(cumulative_gp)) %>%
mutate(ranking = row_number())
food_wage_df2[food_wage_df2 == "Upah Minimum Regional/Propinsi"] <- "Min Wage"
food_wage_df2[food_wage_df2 == "Daging Ayam Broiler"] <- "Chicken"
food_wage_df2[food_wage_df2 == "Cabe Merah"] <- "Red Chili"
food_wage_df2[food_wage_df2 == "Bawang Merah"] <- "Shallot"
food_wage_df2[food_wage_df2 == "Telur Ayam Ras"] <- "Egg"
food_wage_df2[food_wage_df2 == "Gula Pasir Lokal"] <- "Sugar"
food_wage_df2[food_wage_df2 == "Minyak Goreng Kemasan"] <- "Cooking Oil"
food_wage_df2[food_wage_df2 == "Beras"] <- "Rice"
food_wage_df2[food_wage_df2 == "Daging Sapi"] <- "Beef"
datatable(
food_wage_df2,
extensions = 'Scroller',
options = list(
deferRender = TRUE,
scrollY = 200,
scroller = TRUE
)
)
Make a new dataframe which contain the image path for each item, which will be used for visualization. All of the images are downloaded from freepik.com under free license. Beef, egg, and chicken are designed by Terdpongvector. Rice, red chili, and shallot are designed by macrovector. Cooking oil is designed by valadzionak_volha. Sugar is desgined by pch.vector. Wage is desgined by katemangostar.
img_df <- data.frame(food_or_wage = c("Min Wage", "Chicken", "Red Chili", "Shallot", "Egg", "Sugar", "Cooking Oil", "Rice", "Beef"),
image = c("C:/Users/Marselo/Downloads/food images/wage.png",
"C:/Users/Marselo/Downloads/food images/chicken.png",
"C:/Users/Marselo/Downloads/food images/red_chili.png",
"C:/Users/Marselo/Downloads/food images/shallot.png",
"C:/Users/Marselo/Downloads/food images/egg.png",
"C:/Users/Marselo/Downloads/food images/sugar.png",
"C:/Users/Marselo/Downloads/food images/cooking_oil.png",
"C:/Users/Marselo/Downloads/food images/rice.png",
"C:/Users/Marselo/Downloads/food images/beef.png"),
stringsAsFactors = F)
img_df
static_plot <- food_wage_df2 %>%
left_join(img_df, by = "food_or_wage") %>%
ggplot(aes(ranking, cumulative_gp)) +
geom_col(aes(fill = food_or_wage), alpha=0.7) +
coord_flip(clip = "off", expand = FALSE) +
scale_x_reverse() +
scale_y_continuous(labels = function(x) paste0(x, '%'))+
geom_text(aes(ranking, cumulative_gp, label = paste0(scales::number(cumulative_gp, acc = 0.1), "% ")),
hjust = ifelse(food_wage_df2$cumulative_gp < 0, 0, 1), vjust=0.75, size=5) +
geom_text(aes(ranking, cumulative_gp, label = paste0(food_or_wage," ")),
hjust = ifelse(food_wage_df2$cumulative_gp < 0, 0, 1), vjust=-0.75, size=5, fontface="bold") +
geom_image(aes(ranking, y=-40, image = image),
hjust = 0, size=0.08,) +
labs(title='Cumulative Food Inflation and Wage Growth in Indonesia',
subtitle = '2010-{closest_state}', x = "",
y = "Cumulative Percentage Change",
caption = 'Data Source: aplikasi2.pertanian.go.id & bps.go.id. Image Source: freepik.com.') +
theme_minimal() +
theme(
axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.x = element_text(size=12),
plot.title = element_text(hjust = 0.5, size = 24),
plot.subtitle = element_text(hjust = 0.5, size = 24),
plot.caption = element_text(hjust = 0.5, color='gray', size=10),
plot.margin = margin(1,1,1,1, "cm"),
panel.grid.major.y = element_blank(),
panel.grid.minor.y = element_blank(),
panel.grid.minor.x = element_blank()
) +
guides(fill = "none")
animated_plot <- static_plot + transition_states(year, transition_length = 4, state_length = 1) +
ease_aes('cubic-in-out')
gif_animation <- animate(animated_plot,100,fps = 20,duration = 10, width = 1000, height = 1000, renderer = gifski_renderer())
gif_animation

anim_save("food_price_4.gif",animation = gif_animation)
LS0tDQp0aXRsZTogIkZvb2QgSW5mbGF0aW9uIGFuZCBXYWdlIEdyb3d0aCBpbiBJbmRvbmVzaWEiDQphdXRob3I6ICJUaW1vdGl1cyBNYXJzZWxvIg0KZGF0ZTogMjcvMDgvMjAyMg0Kb3V0cHV0Og0KICAgIGh0bWxfbm90ZWJvb2s6DQogICAgICAgIHRvYzogVFJVRQ0KLS0tDQoNCiMgQW5hbHl6ZSBhbmQgdmlzdWFsaXplIHRoZSBmb29kIGluZmxhdGlvbiBhbmQgd2FnZSBncm93dGggaW4gSW5kb25lc2lhIGZyb20gMjAxMCB0byAyMDIxLg0KDQojIyAxLiBJbnN0YWxsIGFuZCBsb2FkIHRoZSBuZWNlc3NhcnkgbGlicmFyeS4NCg0KYGBge3J9DQpsaWJyYXJ5KFJNeVNRTCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShEVCkNCmxpYnJhcnkoZ2dhbmltYXRlKQ0KbGlicmFyeShnZ2ltYWdlKQ0KYGBgDQoNClJNeVNRTCBwYWNrYWdlIGlzIG5lZWRlZCB0byBjb25uZWN0IHRvIE15U1FMIGRhdGFiYXNlLiBUaWR5dmVyc2UgcGFja2FnZSBpcyB1c2VkIGZvciBkYXRhIHByb2Nlc3NpbmcgYW5kIHZpc3VhbGl6YXRpb24uIERUIGltcHJvdmVzIHRoZSBxdWFsaXR5IG9mIHRoZSB0YWJsZSBkaXNwbGF5IG9uIHRoZSBIVE1MIHBhZ2UuDQoNCmBgYHtyIGVjaG8gPSBGQUxTRX0NCnVzZXIgPC0gInJvb3QiDQpwYXNzd29yZCA8LSAicm9vdCINCmBgYA0KDQojIyAyLiBDb25uZWN0IHRvIGxvY2FsIE15U1FMIHNlcnZlci4NCg0KYGBge3J9DQpteWRiIDwtIGRiQ29ubmVjdChSTXlTUUw6Ok15U1FMKCksDQogICAgICAgICAgICAgICAgIGRibmFtZT0nY29tbW9kaXR5X3ByaWNlJywNCiAgICAgICAgICAgICAgICAgaG9zdD0nbG9jYWxob3N0JywNCiAgICAgICAgICAgICAgICAgcG9ydD0zMzA2LA0KICAgICAgICAgICAgICAgICB1c2VyID0gdXNlciwNCiAgICAgICAgICAgICAgICAgcGFzc3dvcmQgPSBwYXNzd29yZCkNCmBgYA0KDQojIyAzLiBTaG93IHRoZSB0YWJsZXMgaW5zaWRlIHRoZSBkYXRhYmFzZS4NCg0KYGBge3J9DQpkYkxpc3RUYWJsZXMobXlkYikNCmBgYA0KVGhlIHJhdyBkYXRhIGZvciBmb29kIGNvbW1vZGl0eSBwcmljZSBjYW4gYmUgZG93bmxvYWRlZCBvbiBodHRwczovL2FwbGlrYXNpMi5wZXJ0YW5pYW4uZ28uaWQvc2ltaGFyZ2EyMDE3L2tvbnN1bWVuX2tvdGEvaGtwNC4gVGhlIHJhdyBkYXRhIGZvciBtaW5pbXVtIHdhZ2UgKGV4Y2VwdCBmb3IgMjAxNyBhbmQgMjAyMSkgY2FuIGJlIGRvd25sb2FkZWQgb24gaHR0cHM6Ly93d3cuYnBzLmdvLmlkL2luZGljYXRvci8xOS8yMjAvMS91cGFoLW1pbmltdW0tcmVnaW9uYWwtcHJvcGluc2kuaHRtbC4gVGhlIGF2ZXJhZ2UgbWluaW11bSB3YWdlIGZvciAyMDE3IGFuZCAyMDIxIGlzIGNhbGN1bGF0ZWQgZnJvbSBodHRwczovL2ZpbmFuY2UuZGV0aWsuY29tL2Jlcml0YS1la29ub21pLWJpc25pcy9kLTMzNTY3NjIvaW5pLWRpYS1kYWZ0YXItbGVuZ2thcC11bXAtMjAxNy1kaS0zNC1wcm92aW5zaSBhbmQgaHR0cHM6Ly9maW5hbmNlLmRldGlrLmNvbS9iZXJpdGEtZWtvbm9taS1iaXNuaXMvZC01MzI2OTE4L2xlbmdrYXAtcmluY2lhbi11bXAtMjAyMS1kaS0zNC1wcm92aW5zaS4NCg0KIyMgNC4gV3JpdGUgYSBxdWVyeSB0byBzZWxlY3QgYWxsIHJvd3MgYW5kIGNvbHVtbnMgZnJvbSB0aGUgZm9vZF9jb21tb2RpdHlfcHJpY2UgYW5kIG1pbmltdW1fd2FnZSB0YWJsZSwgdGhlbiBzaG93IHRoZSByZXN1bHRzLg0KDQpgYGB7cn0NCnJlc3VsdF8xIDwtIGRiR2V0UXVlcnkobXlkYiwgIlNFTEVDVCAqIEZST00gZm9vZF9jb21tb2RpdHlfcHJpY2UiKQ0KDQpkYXRhdGFibGUoDQogIHJlc3VsdF8xLA0KICBleHRlbnNpb25zID0gIkZpeGVkQ29sdW1ucyIsDQogIG9wdGlvbnMgPSBsaXN0KA0KICAgIHBhZ2luZyA9IFRSVUUsIHNlYXJjaGluZyA9IFRSVUUsDQogICAgc2Nyb2xsWCA9IFRSVUUsIGZpeGVkQ29sdW1ucyA9IGxpc3QobGVmdENvbHVtbnMgPSAyKQ0KICApDQopDQpgYGANCg0KYGBge3J9DQpyZXN1bHRfMiA8LSBkYkdldFF1ZXJ5KG15ZGIsICJTRUxFQ1QgKiBGUk9NIG1pbmltdW1fd2FnZSIpDQoNCmRhdGF0YWJsZSgNCiAgcmVzdWx0XzIsDQogIGV4dGVuc2lvbnMgPSAiRml4ZWRDb2x1bW5zIiwNCiAgb3B0aW9ucyA9IGxpc3QoDQogICAgcGFnaW5nID0gVFJVRSwgc2VhcmNoaW5nID0gVFJVRSwNCiAgICBzY3JvbGxYID0gVFJVRSwgZml4ZWRDb2x1bW5zID0gbGlzdChsZWZ0Q29sdW1ucyA9IDIpDQogICkNCikNCmBgYA0KDQojIyA1LiBUaGUgZGF0YSBmb3IgZm9vZCBjb21tb2RpdHkgcHJpY2UgYXJlIHdyaXR0ZW4gaW4gd2lkZSBmb3JtYXQsIHNvIGxldCdzIGNvbnZlcnQgdGhlIGRhdGEgaW50byBsb25nIGZvcm1hdCwgdGhlbiBzYXZlIHRoZSBkYXRhIGFzIGEgdmlldy4NCg0KYGBge3J9DQpkYkdldFF1ZXJ5KG15ZGIsICJEUk9QIFZJRVcgSUYgRVhJU1RTIGZjcF9sb25nIikNCmRiR2V0UXVlcnkobXlkYiwgIkNSRUFURSBWSUVXIGZjcF9sb25nIEFTDQogICAgICAgICAgICAgICAgICAgICAgICAgICBTRUxFQ1QgYEtvbW9kaXRhcyBQYW5nYW5gIEFTIGZvb2RfY29tbW9kaXR5LCAnMjAxMCcgQVMgeWVhciwgYDIwMTBgIEFTIHByaWNlX3Blcl9rZw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgRlJPTSBmb29kX2NvbW1vZGl0eV9wcmljZSBVTklPTiBBTEwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFNFTEVDVCBgS29tb2RpdGFzIFBhbmdhbmAgQVMgZm9vZF9jb21tb2RpdHksICcyMDExJyBBUyB5ZWFyLCBgMjAxMWAgQVMgcHJpY2VfcGVyX2tnDQogICAgICAgICAgICAgICAgICAgICAgICAgICBGUk9NIGZvb2RfY29tbW9kaXR5X3ByaWNlIFVOSU9OIEFMTA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgU0VMRUNUIGBLb21vZGl0YXMgUGFuZ2FuYCBBUyBmb29kX2NvbW1vZGl0eSwgJzIwMTInIEFTIHllYXIsIGAyMDEyYCBBUyBwcmljZV9wZXJfa2cNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIEZST00gZm9vZF9jb21tb2RpdHlfcHJpY2UgVU5JT04gQUxMDQogICAgICAgICAgICAgICAgICAgICAgICAgICBTRUxFQ1QgYEtvbW9kaXRhcyBQYW5nYW5gIEFTIGZvb2RfY29tbW9kaXR5LCAnMjAxMycgQVMgeWVhciwgYDIwMTNgIEFTIHByaWNlX3Blcl9rZw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgRlJPTSBmb29kX2NvbW1vZGl0eV9wcmljZSBVTklPTiBBTEwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFNFTEVDVCBgS29tb2RpdGFzIFBhbmdhbmAgQVMgZm9vZF9jb21tb2RpdHksICcyMDE0JyBBUyB5ZWFyLCBgMjAxNGAgQVMgcHJpY2VfcGVyX2tnDQogICAgICAgICAgICAgICAgICAgICAgICAgICBGUk9NIGZvb2RfY29tbW9kaXR5X3ByaWNlIFVOSU9OIEFMTA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgU0VMRUNUIGBLb21vZGl0YXMgUGFuZ2FuYCBBUyBmb29kX2NvbW1vZGl0eSwgJzIwMTUnIEFTIHllYXIsIGAyMDE1YCBBUyBwcmljZV9wZXJfa2cNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIEZST00gZm9vZF9jb21tb2RpdHlfcHJpY2UgVU5JT04gQUxMDQogICAgICAgICAgICAgICAgICAgICAgICAgICBTRUxFQ1QgYEtvbW9kaXRhcyBQYW5nYW5gIEFTIGZvb2RfY29tbW9kaXR5LCAnMjAxNicgQVMgeWVhciwgYDIwMTZgIEFTIHByaWNlX3Blcl9rZw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgRlJPTSBmb29kX2NvbW1vZGl0eV9wcmljZSBVTklPTiBBTEwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFNFTEVDVCBgS29tb2RpdGFzIFBhbmdhbmAgQVMgZm9vZF9jb21tb2RpdHksICcyMDE3JyBBUyB5ZWFyLCBgMjAxN2AgQVMgcHJpY2VfcGVyX2tnDQogICAgICAgICAgICAgICAgICAgICAgICAgICBGUk9NIGZvb2RfY29tbW9kaXR5X3ByaWNlIFVOSU9OIEFMTA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgU0VMRUNUIGBLb21vZGl0YXMgUGFuZ2FuYCBBUyBmb29kX2NvbW1vZGl0eSwgJzIwMTgnIEFTIHllYXIsIGAyMDE4YCBBUyBwcmljZV9wZXJfa2cNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIEZST00gZm9vZF9jb21tb2RpdHlfcHJpY2UgVU5JT04gQUxMDQogICAgICAgICAgICAgICAgICAgICAgICAgICBTRUxFQ1QgYEtvbW9kaXRhcyBQYW5nYW5gIEFTIGZvb2RfY29tbW9kaXR5LCAnMjAxOScgQVMgeWVhciwgYDIwMTlgIEFTIHByaWNlX3Blcl9rZw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgRlJPTSBmb29kX2NvbW1vZGl0eV9wcmljZSBVTklPTiBBTEwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFNFTEVDVCBgS29tb2RpdGFzIFBhbmdhbmAgQVMgZm9vZF9jb21tb2RpdHksICcyMDIwJyBBUyB5ZWFyLCBgMjAyMGAgQVMgcHJpY2VfcGVyX2tnDQogICAgICAgICAgICAgICAgICAgICAgICAgICBGUk9NIGZvb2RfY29tbW9kaXR5X3ByaWNlIFVOSU9OIEFMTA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgU0VMRUNUIGBLb21vZGl0YXMgUGFuZ2FuYCBBUyBmb29kX2NvbW1vZGl0eSwgJzIwMjEnIEFTIHllYXIsIGAyMDIxYCBBUyBwcmljZV9wZXJfa2cNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIEZST00gZm9vZF9jb21tb2RpdHlfcHJpY2UNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIE9SREVSIEJZIGZvb2RfY29tbW9kaXR5LCB5ZWFyIikNCmBgYA0KDQojIyA2LiBTaG93IHRoZSByZXN1bHRzIG9mIHRoZSB2aWV3Lg0KDQpgYGB7cn0NCnJlc3VsdF8zIDwtIGRiR2V0UXVlcnkobXlkYiwgIlNFTEVDVCAqIEZST00gZmNwX2xvbmciKQ0KDQpkYXRhdGFibGUoDQogIHJlc3VsdF8zLA0KICBleHRlbnNpb25zID0gJ1Njcm9sbGVyJywNCiAgb3B0aW9ucyA9IGxpc3QoDQogICAgZGVmZXJSZW5kZXIgPSBUUlVFLA0KICAgIHNjcm9sbFkgPSAyMDAsDQogICAgc2Nyb2xsZXIgPSBUUlVFDQogICkNCikNCmBgYA0KDQoNCiMjIDcuIEZvciB2aXN1YWxpemF0aW9uIHB1cnBvc2UsIHRoZSBmb29kIGNvbW1vZGl0eSB3aXRoIGFueSBudWxsIHZhbHVlIGluIHRoZSBwcmljZSBjb2x1bW4gd2lsbCBiZSBleGNsdWRlZC4NCmBgYHtyfQ0KcmVzdWx0XzQgPC0gZGJHZXRRdWVyeShteWRiLCAiU0VMRUNUIGZvb2RfY29tbW9kaXR5IEZST00gZmNwX2xvbmcgR1JPVVAgQlkgMSBIQVZJTkcgTUlOKHByaWNlX3Blcl9rZykgPD4gMCIpDQpyZXN1bHRfNA0KYGBgDQoNClRoZSBmb2xsb3dpbmcgZm9vZCBjb21tb2RpdGllcyBoYXZlIG5vIG51bGwgdmFsdWUgaW4gdGhlIHByaWNlIGNvbHVtbjogc2hhbGxvdCAoYmF3YW5nIG1lcmFoKSwgcmljZSAoYmVyYXMpLCByZWQgY2hpbGkgKGNhYmUgbWVyYWgpLCBjdXJseSByZWQgY2hpbGkgKGNhYmUgbWVyYWgga2VyaXRpbmcpLCBjaGlja2VuIG1lYXQgKGRhZ2luZyBheWFtIGJyb2lsZXIpLCBiZWVmIChkYWdpbmcgc2FwaSksIHdoaXRlIHN1Z2FyIChndWxhIHBhc2lyIGxva2FsKSwgY29va2luZyBvaWwgKG1pbnlhayBnb3Jlbmcga2VtYXNhbiksIGFuZCBjaGlja2VuIGVnZyAodGVsdXIgYXlhbSByYXMpLiBJIGFtIGdvaW5nIHRvIGV4Y2x1ZGUgY3VybHkgcmVkIGNoaWxpIGJlY2F1c2UgaXQncyB2ZXJ5IHNpbWlsYXIgdG8gcmVkIGNoaWxpLg0KDQojIyA4LiBDb21iaW5lIHRoZSBtaW5pbXVtIHdhZ2UgZGF0YSB3aXRoIHRoZSBmb29kIHByaWNlIHVzaW5nIFVOSU9OLiBUaGVuIGNhbGN1bGF0ZSB0aGUgWW9ZIGFuZCBjdW11bGF0aXZlIGdyb3d0aCBwZXJjZW50YWdlIGZvciBmb29kJ3MgcHJpY2UgYW5kIG1pbmltdW0gd2FnZS4NCmBgYHtyfQ0KZm9vZF93YWdlX2RmIDwtIGRiR2V0UXVlcnkobXlkYiwgIldJVEggZm9vZF93YWdlIEFTIChTRUxFQ1QgKg0KICAgICAgICAgICAgICAgICAgRlJPTSBmY3BfbG9uZw0KICAgICAgICAgICAgICAgICAgV0hFUkUgZm9vZF9jb21tb2RpdHkgPD4gJ0NhYmUgTWVyYWggS2VyaXRpbmcnDQogICAgICAgICAgICAgICAgICAgIEFORCBmb29kX2NvbW1vZGl0eSBJTiAoU0VMRUNUIGZvb2RfY29tbW9kaXR5DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRlJPTSBmY3BfbG9uZw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdST1VQIEJZIDENCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBIQVZJTkcgTUlOKHByaWNlX3Blcl9rZykgPD4gMCkNCiAgICAgICAgICAgICAgICAgIFVOSU9ODQogICAgICAgICAgICAgICAgICBTRUxFQ1QgbmFtYV92YXJpYWJlbCwgbmFtYV90YWh1biwgZGF0YV9jb250ZW50DQogICAgICAgICAgICAgICAgICBGUk9NIG1pbmltdW1fd2FnZQ0KICAgICAgICAgICAgICAgICAgT1JERVIgQlkgMSwgMikNCiAgICAgICAgICAgICAgICAgIFNFTEVDVCBmb29kX2NvbW1vZGl0eSBBUyBmb29kX29yX3dhZ2UsIENBU1QoeWVhciBBUyBTSUdORUQpIGFzIHllYXIsDQogICAgICAgICAgICAgICAgICAgICAgICBwcmljZV9wZXJfa2cgQVMgcHJpY2VfcGVyX2tnX29yX21pbmltdW1fd2FnZSwNCiAgICAgICAgICAgICAgICAgICAgICAgIChwcmljZV9wZXJfa2cgLSAoTEFHKHByaWNlX3Blcl9rZykgT1ZFUiAoUEFSVElUSU9OIEJZIGZvb2RfY29tbW9kaXR5IE9SREVSIEJZIHllYXIpKSkgLw0KICAgICAgICAgICAgICAgICAgICAgICAgKExBRyhwcmljZV9wZXJfa2cpIE9WRVIgKFBBUlRJVElPTiBCWSBmb29kX2NvbW1vZGl0eSBPUkRFUiBCWSB5ZWFyKSkgKiAxMDAgQVMgZ3Jvd3RoX3BlcmNlbnRhZ2UsDQogICAgICAgICAgICAgICAgICAgICAgICAocHJpY2VfcGVyX2tnIC0gKEZJUlNUX1ZBTFVFKHByaWNlX3Blcl9rZykgT1ZFUiAoUEFSVElUSU9OIEJZIGZvb2RfY29tbW9kaXR5IE9SREVSIEJZIHllYXIpKSkgLw0KICAgICAgICAgICAgICAgICAgICAgICAgKEZJUlNUX1ZBTFVFKHByaWNlX3Blcl9rZykgT1ZFUiAoUEFSVElUSU9OIEJZIGZvb2RfY29tbW9kaXR5IE9SREVSIEJZIHllYXIpKSAqIDEwMCBBUyBjdW11bGF0aXZlX2dwDQogICAgICAgICAgICAgICAgICBGUk9NIGZvb2Rfd2FnZSIpDQoNCmRhdGF0YWJsZSgNCiAgZm9vZF93YWdlX2RmLA0KICBleHRlbnNpb25zID0gJ1Njcm9sbGVyJywNCiAgb3B0aW9ucyA9IGxpc3QoDQogICAgZGVmZXJSZW5kZXIgPSBUUlVFLA0KICAgIHNjcm9sbFkgPSAyMDAsDQogICAgc2Nyb2xsZXIgPSBUUlVFDQogICkNCikNCmBgYA0KDQpDQVNUIGZ1bmN0aW9uIGlzIHVzZWQgdG8gY2hhbmdlIHRoZSB5ZWFyIHRvIHNpZ25lZCBpbnRlZ2VyLiBUaGlzIGFsbG93cyBSIHRvIHNhdmUgdGhlbSBhcyBkYmwgKGRvdWJsZS1wcmVjaXNpb24gZmxvYXRpbmcgcG9pbnQgbnVtYmVyKSBpbnN0ZWFkIG9mIGNociAoY2hhcmFjdGVycykuDQoNCiMjIDkuIFZpc3VhbGl6ZSB0aGUgY3VtdWxhdGl2ZSBncm93dGggcGVyY2VudGFnZSBmb3IgZm9vZCdzIHByaWNlIGFuZCBtaW5pbXVtIHdhZ2Ugc2luY2UgMjAxMC4NCg0KYGBge3IgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9MTR9DQpmb29kX3dhZ2VfZGZbaXMubmEoZm9vZF93YWdlX2RmKV0gPC0gMA0KZm9vZF93YWdlX2RmICU+JQ0KICBnZ3Bsb3QoYWVzKGN1bXVsYXRpdmVfZ3AsIGZvb2Rfb3Jfd2FnZSkpICsNCiAgZ2VvbV9jb2woYWVzKGZpbGwgPSBmb29kX29yX3dhZ2UpKSArDQogIGZhY2V0X3dyYXAodmFycyh5ZWFyKSkNCmBgYA0KDQpNYWtlIGEgbmV3IGRhdGFmcmFtZSB0byByYW5rIHRoZSBmb29kIGNvbW1vZGl0aWVzIGFuZCB3YWdlIGJhc2VkIG9uIHRoZSBjdW11bGF0aXZlIHBlcmNlbnQgZ3Jvd3RoIGZvciBlYWNoIHllYXIsIGFuZCB0cmFuc2xhdGUgdGhlIGl0ZW1zIGZyb20gSW5kb25lc2lhbiB0byBFbmdsaXNoLg0KDQpgYGB7cn0NCmZvb2Rfd2FnZV9kZjIgPC0gZm9vZF93YWdlX2RmICU+JQ0KICBncm91cF9ieSh5ZWFyKSAlPiUNCiAgYXJyYW5nZSh5ZWFyLCBkZXNjKGN1bXVsYXRpdmVfZ3ApKSAlPiUNCiAgbXV0YXRlKHJhbmtpbmcgPSByb3dfbnVtYmVyKCkpDQoNCmZvb2Rfd2FnZV9kZjJbZm9vZF93YWdlX2RmMiA9PSAiVXBhaCBNaW5pbXVtIFJlZ2lvbmFsL1Byb3BpbnNpIl0gPC0gIk1pbiBXYWdlIg0KZm9vZF93YWdlX2RmMltmb29kX3dhZ2VfZGYyID09ICJEYWdpbmcgQXlhbSBCcm9pbGVyIl0gPC0gIkNoaWNrZW4iDQpmb29kX3dhZ2VfZGYyW2Zvb2Rfd2FnZV9kZjIgPT0gIkNhYmUgTWVyYWgiXSA8LSAiUmVkIENoaWxpIg0KZm9vZF93YWdlX2RmMltmb29kX3dhZ2VfZGYyID09ICJCYXdhbmcgTWVyYWgiXSA8LSAiU2hhbGxvdCINCmZvb2Rfd2FnZV9kZjJbZm9vZF93YWdlX2RmMiA9PSAiVGVsdXIgQXlhbSBSYXMiXSA8LSAiRWdnIg0KZm9vZF93YWdlX2RmMltmb29kX3dhZ2VfZGYyID09ICJHdWxhIFBhc2lyIExva2FsIl0gPC0gIlN1Z2FyIg0KZm9vZF93YWdlX2RmMltmb29kX3dhZ2VfZGYyID09ICJNaW55YWsgR29yZW5nIEtlbWFzYW4iXSA8LSAiQ29va2luZyBPaWwiDQpmb29kX3dhZ2VfZGYyW2Zvb2Rfd2FnZV9kZjIgPT0gIkJlcmFzIl0gPC0gIlJpY2UiDQpmb29kX3dhZ2VfZGYyW2Zvb2Rfd2FnZV9kZjIgPT0gIkRhZ2luZyBTYXBpIl0gPC0gIkJlZWYiDQoNCmRhdGF0YWJsZSgNCiAgZm9vZF93YWdlX2RmMiwNCiAgZXh0ZW5zaW9ucyA9ICdTY3JvbGxlcicsDQogIG9wdGlvbnMgPSBsaXN0KA0KICAgIGRlZmVyUmVuZGVyID0gVFJVRSwNCiAgICBzY3JvbGxZID0gMjAwLA0KICAgIHNjcm9sbGVyID0gVFJVRQ0KICApDQopDQpgYGANCg0KTWFrZSBhIG5ldyBkYXRhZnJhbWUgd2hpY2ggY29udGFpbiB0aGUgaW1hZ2UgcGF0aCBmb3IgZWFjaCBpdGVtLCB3aGljaCB3aWxsIGJlIHVzZWQgZm9yIHZpc3VhbGl6YXRpb24uIEFsbCBvZiB0aGUgaW1hZ2VzIGFyZSBkb3dubG9hZGVkIGZyb20gZnJlZXBpay5jb20gdW5kZXIgZnJlZSBsaWNlbnNlLiBCZWVmLCBlZ2csIGFuZCBjaGlja2VuIGFyZSBkZXNpZ25lZCBieSBUZXJkcG9uZ3ZlY3Rvci4gUmljZSwgcmVkIGNoaWxpLCBhbmQgc2hhbGxvdCBhcmUgZGVzaWduZWQgYnkgbWFjcm92ZWN0b3IuIENvb2tpbmcgb2lsIGlzIGRlc2lnbmVkIGJ5IHZhbGFkemlvbmFrX3ZvbGhhLiBTdWdhciBpcyBkZXNnaW5lZCBieSBwY2gudmVjdG9yLiBXYWdlIGlzIGRlc2dpbmVkIGJ5IGthdGVtYW5nb3N0YXIuDQoNCmBgYHtyfQ0KaW1nX2RmIDwtIGRhdGEuZnJhbWUoZm9vZF9vcl93YWdlID0gYygiTWluIFdhZ2UiLCAiQ2hpY2tlbiIsICJSZWQgQ2hpbGkiLCAiU2hhbGxvdCIsICJFZ2ciLCAiU3VnYXIiLCAiQ29va2luZyBPaWwiLCAiUmljZSIsICJCZWVmIiksDQogICAgICAgICAgICAgICAgICAgICBpbWFnZSA9IGMoIkM6L1VzZXJzL01hcnNlbG8vRG93bmxvYWRzL2Zvb2QgaW1hZ2VzL3dhZ2UucG5nIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQzovVXNlcnMvTWFyc2Vsby9Eb3dubG9hZHMvZm9vZCBpbWFnZXMvY2hpY2tlbi5wbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDOi9Vc2Vycy9NYXJzZWxvL0Rvd25sb2Fkcy9mb29kIGltYWdlcy9yZWRfY2hpbGkucG5nIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQzovVXNlcnMvTWFyc2Vsby9Eb3dubG9hZHMvZm9vZCBpbWFnZXMvc2hhbGxvdC5wbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDOi9Vc2Vycy9NYXJzZWxvL0Rvd25sb2Fkcy9mb29kIGltYWdlcy9lZ2cucG5nIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQzovVXNlcnMvTWFyc2Vsby9Eb3dubG9hZHMvZm9vZCBpbWFnZXMvc3VnYXIucG5nIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQzovVXNlcnMvTWFyc2Vsby9Eb3dubG9hZHMvZm9vZCBpbWFnZXMvY29va2luZ19vaWwucG5nIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQzovVXNlcnMvTWFyc2Vsby9Eb3dubG9hZHMvZm9vZCBpbWFnZXMvcmljZS5wbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDOi9Vc2Vycy9NYXJzZWxvL0Rvd25sb2Fkcy9mb29kIGltYWdlcy9iZWVmLnBuZyIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQ0KaW1nX2RmDQpgYGANCg0KYGBge3J9DQpzdGF0aWNfcGxvdCA8LSBmb29kX3dhZ2VfZGYyICU+JQ0KICBsZWZ0X2pvaW4oaW1nX2RmLCBieSA9ICJmb29kX29yX3dhZ2UiKSAlPiUNCiAgZ2dwbG90KGFlcyhyYW5raW5nLCBjdW11bGF0aXZlX2dwKSkgKw0KICBnZW9tX2NvbChhZXMoZmlsbCA9IGZvb2Rfb3Jfd2FnZSksIGFscGhhPTAuNykgKw0KICBjb29yZF9mbGlwKGNsaXAgPSAib2ZmIiwgZXhwYW5kID0gRkFMU0UpICsNCiAgc2NhbGVfeF9yZXZlcnNlKCkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gZnVuY3Rpb24oeCkgcGFzdGUwKHgsICclJykpKw0KICBnZW9tX3RleHQoYWVzKHJhbmtpbmcsIGN1bXVsYXRpdmVfZ3AsIGxhYmVsID0gcGFzdGUwKHNjYWxlczo6bnVtYmVyKGN1bXVsYXRpdmVfZ3AsIGFjYyA9IDAuMSksICIlICIpKSwNCiAgICAgICAgICAgIGhqdXN0ID0gaWZlbHNlKGZvb2Rfd2FnZV9kZjIkY3VtdWxhdGl2ZV9ncCA8IDAsIDAsIDEpLCB2anVzdD0wLjc1LCBzaXplPTUpICsNCiAgZ2VvbV90ZXh0KGFlcyhyYW5raW5nLCBjdW11bGF0aXZlX2dwLCBsYWJlbCA9IHBhc3RlMChmb29kX29yX3dhZ2UsIiAiKSksDQogICAgICAgICAgICBoanVzdCA9IGlmZWxzZShmb29kX3dhZ2VfZGYyJGN1bXVsYXRpdmVfZ3AgPCAwLCAwLCAxKSwgdmp1c3Q9LTAuNzUsIHNpemU9NSwgZm9udGZhY2U9ImJvbGQiKSArDQogIGdlb21faW1hZ2UoYWVzKHJhbmtpbmcsIHk9LTQwLCBpbWFnZSA9IGltYWdlKSwNCiAgICAgICAgICAgICBoanVzdCA9IDAsIHNpemU9MC4wOCwpICsNCiAgbGFicyh0aXRsZT0nQ3VtdWxhdGl2ZSBGb29kIEluZmxhdGlvbiBhbmQgV2FnZSBHcm93dGggaW4gSW5kb25lc2lhJywNCiAgICAgICBzdWJ0aXRsZSA9ICcyMDEwLXtjbG9zZXN0X3N0YXRlfScsIHggPSAiIiwNCiAgICAgICB5ID0gIkN1bXVsYXRpdmUgUGVyY2VudGFnZSBDaGFuZ2UiLA0KICAgICAgIGNhcHRpb24gPSAnRGF0YSBTb3VyY2U6IGFwbGlrYXNpMi5wZXJ0YW5pYW4uZ28uaWQgJiBicHMuZ28uaWQuIEltYWdlIFNvdXJjZTogZnJlZXBpay5jb20uJykgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9MTIpLA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAyNCksDQogICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDI0KSwNCiAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGNvbG9yPSdncmF5Jywgc2l6ZT0xMCksDQogICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMSwxLDEsMSwgImNtIiksDQogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCkNCiAgKSArDQogIGd1aWRlcyhmaWxsID0gIm5vbmUiKQ0KDQphbmltYXRlZF9wbG90IDwtIHN0YXRpY19wbG90ICsgdHJhbnNpdGlvbl9zdGF0ZXMoeWVhciwgdHJhbnNpdGlvbl9sZW5ndGggPSA0LCBzdGF0ZV9sZW5ndGggPSAxKSArDQogIGVhc2VfYWVzKCdjdWJpYy1pbi1vdXQnKQ0KDQpnaWZfYW5pbWF0aW9uIDwtIGFuaW1hdGUoYW5pbWF0ZWRfcGxvdCwxMDAsZnBzID0gMjAsZHVyYXRpb24gPSAxMCwgd2lkdGggPSAxMDAwLCBoZWlnaHQgPSAxMDAwLCByZW5kZXJlciA9IGdpZnNraV9yZW5kZXJlcigpKQ0KZ2lmX2FuaW1hdGlvbg0KYW5pbV9zYXZlKCJmb29kX3ByaWNlXzQuZ2lmIixhbmltYXRpb24gPSBnaWZfYW5pbWF0aW9uKQ0KYGBgDQo=